package setting

import (
	"fmt"
	"net/http"
	"os"
	"path"
	"strings"

	"gopkg.in/go-module/log.v1"
	"gopkg.in/ini.v1"

	"gitee.com/Flearning/go_crawl/utils"
)

const (
	APP_VER  = "1.4.1.0825"
	CFG_PATH = "conf/conf.ini" //配置
	LOG_PATH = "crawldata.log"
)

type (
	MgoInfo struct {
		Hosts    string
		Database string
		UserName string
		Password string
	}

	CrawlRate struct {
		Rate               int
		StartTime, EndTime string
		Weeks              []int
	}

	CrawlInfo struct {
		CrawlUri string
		Args     []map[string]interface{}
	}

	HttpClientReqInfo struct {
		Header http.Header
	}
)

var (
	ConfPath           string // 配置文件路径
	AppName, AppVer    string
	HttpAddr, HttpPort string
	RunMode            string // 运行模式
	RunHttp            string // 运行http
	CmdRoot            string

	Sites []string

	// Global setting objects
	Cfg       *ini.File
	ProdMode  bool
	HttpRun   bool
	dbSecName string

	// Log settings.
	LogRootPath string
	LogModes    []string
	LogConfigs  []string

	InfoMgo   *MgoInfo
	RateCrawl *CrawlRate
)

func init() {
	log.NewLogger(0, "console", `{"level": 0}`)
}

func forcePathSeparator(path string) {
	if strings.Contains(path, "\\") {
		log.Fatal(4, "Do not use '\\' or '\\\\' in paths, instead, please use '/' in all places")
	}
}

func NewConfigContext() {
	ConfPath = CFG_PATH
	//检测目录是否存在
	workDir, err := utils.WorkDir()
	if err != nil {
		log.Fatal(4, "Fail to get work directory: %v", err)
	}

	//判断INI文件是否存在
	if !utils.IsFile(ConfPath) {
		log.Warn("System config (%s) not found, ignore this if you're running first time", ConfPath)
	}
	// 加载配置文件
	Cfg, err = ini.Load(ConfPath)
	if err != nil {
		log.Fatal(4, "Fail to parse '%s': %v", CFG_PATH, err)
	}
	// 读取log段配置
	LogRootPath = Cfg.Section("log").Key("ROOT_PATH").MustString(path.Join(workDir, "log"))
	forcePathSeparator(LogRootPath)

	// 读取system段配置
	sec := Cfg.Section("system")
	AppName = sec.Key("app_name").MustString("crawl data software.")
	AppVer = APP_VER
	HttpAddr = sec.Key("http_addr").MustString("0.0.0.0")
	HttpPort = sec.Key("http_port").MustString("9999")
	RunMode = sec.Key("run_mode").MustString("dev")
	RunHttp = sec.Key("run_http").MustString("not")

	switch RunMode {
	case "dev", "test":
		ProdMode = false
		dbSecName = "database." + RunMode
	case "prod":
		ProdMode = true
		dbSecName = "database"
	}

	switch RunHttp {
	case "not", "no", "off", "false":
		HttpRun = false
	case "yes", "on", "true":
		HttpRun = true
	}

	// 读取crawlrate段配置
	sec = Cfg.Section("crawlrate")
	RateCrawl = new(CrawlRate)
	RateCrawl.Rate = sec.Key("rate").MustInt(30)
	RateCrawl.StartTime = sec.Key("start_time").MustString("07:00:00")
	RateCrawl.EndTime = sec.Key("end_time").MustString("22:00:00")
	RateCrawl.Weeks = sec.Key("weeks").Ints(",")

	// 读取database段配置
	sec = Cfg.Section(dbSecName)
	InfoMgo = new(MgoInfo)
	InfoMgo.Hosts = sec.Key("Host").MustString("127.0.0.1:27017")
	InfoMgo.Database = sec.Key("Data").MustString("crawl_data")
	InfoMgo.UserName = sec.Key("User").String()
	InfoMgo.Password = sec.Key("Passwd").String()

	// 读取website段配置
	sec = Cfg.Section("website")
	Sites = sec.Key("sites").Strings(",")
}

var logLevels = map[string]string{
	"Trace":    "0",
	"Debug":    "1",
	"Info":     "2",
	"Warn":     "3",
	"Error":    "4",
	"Critical": "5",
}

func NewLogService() {
	log.Info("%s ver: %s", AppName, AppVer)

	// Get and check log mode.
	LogModes = strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",")
	LogConfigs = make([]string, len(LogModes))

	for i, mode := range LogModes {
		mode = strings.TrimSpace(mode)
		sec, err := Cfg.GetSection("log." + mode)
		if err != nil {
			log.Fatal(4, "Unknown log mode: %s", mode)
		}

		validLevels := []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"}
		// Log level.
		levelName := Cfg.Section("log."+mode).Key("LEVEL").In(
			Cfg.Section("log").Key("LEVEL").In("Trace", validLevels),
			validLevels)
		level, ok := logLevels[levelName]
		if !ok {
			log.Fatal(4, "Unknown log level: %s", levelName)
		}

		// Generate log configuration.
		switch mode {
		case "console":
			LogConfigs[i] = fmt.Sprintf(`{"level":%s}`, level)
		case "file":
			logPath := sec.Key("FILE_NAME").MustString(path.Join(LogRootPath, LOG_PATH))
			os.MkdirAll(path.Dir(logPath), os.ModePerm)
			LogConfigs[i] = fmt.Sprintf(
				`{"level":%s,"filename":"%s","rotate":%v,"maxlines":%d,"maxsize":%d,"daily":%v,"maxdays":%d}`, level,
				logPath,
				sec.Key("LOG_ROTATE").MustBool(true),
				sec.Key("MAX_LINES").MustInt(1000000),
				1<<uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),
				sec.Key("DAILY_ROTATE").MustBool(true),
				sec.Key("MAX_DAYS").MustInt(7))
		case "conn":
			LogConfigs[i] = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":"%s","addr":"%s"}`, level,
				sec.Key("RECONNECT_ON_MSG").MustBool(),
				sec.Key("RECONNECT").MustBool(),
				sec.Key("PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"}),
				sec.Key("ADDR").MustString(":7020"))
		case "smtp":
			LogConfigs[i] = fmt.Sprintf(`{"level":%s,"username":"%s","password":"%s","host":"%s","sendTos":"%s","subject":"%s"}`, level,
				sec.Key("USER").MustString("example@example.com"),
				sec.Key("PASSWD").MustString("******"),
				sec.Key("HOST").MustString("127.0.0.1:25"),
				sec.Key("RECEIVERS").MustString("[]"),
				sec.Key("SUBJECT").MustString("Diagnostic message from serve"))
		case "database":
			LogConfigs[i] = fmt.Sprintf(`{"level":%s,"driver":"%s","conn":"%s"}`, level,
				sec.Key("DRIVER").String(),
				sec.Key("CONN").String())
		}

		log.NewLogger(Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000), mode, LogConfigs[i])
		log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName)
	}
}
